home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / DDJ9205.ARJ / CRCMAN.C < prev    next >
C/C++ Source or Header  |  1991-10-08  |  12KB  |  405 lines

  1. /************************** Start of CRCMAN.C *************************
  2.  *
  3.  * This program is used to build a list of CRC-32 values for all of the
  4.  * files in a given directory tree.  After building the file, the program
  5.  * can be run later to verify the CRC values, giving assurance of the
  6.  * integrity of the files.  To build the CRC file, the command line is:
  7.  *
  8.  *   CRCMAN -b root-dir crc-file-name
  9.  *
  10.  * To check the list of files created, run with this command line:
  11.  *
  12.  *   CRCMAN crc-file-name
  13.  *
  14.  * This program should work with most 16 and 32 bit compilers under
  15.  * MS-DOS and UNIX.
  16.  */
  17.  
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <sys/types.h>
  22. #include <sys/stat.h>
  23.  
  24.  
  25. unsigned long CRCTable[ 256 ];
  26.  
  27. /*
  28.  * To build this program under UNIX, define UNIX either on the command
  29.  * line or by editing this file.  To define it on the command line, the
  30.  * program should be built like this:
  31.  *
  32.  *  cc -o crcman -DUNIX crcman.c
  33.  *
  34.  * The code in this program assumes that the UNIX compiler is of the
  35.  * K&R variety, and does away with real function prototyping.
  36.  */
  37.  
  38. #ifdef UNIX
  39.  
  40. #include <varargs.h>
  41. #ifdef M_XENIX
  42. #include <sys/ndir.h>
  43. #else
  44. #include <sys/dirent.h>
  45. #endif /* M_XENIX */
  46.  
  47. #define SEPARATOR "/"
  48. #define FILENAME_SIZE 81
  49.  
  50. void FatalError();
  51. unsigned long CalculateFileCRC();
  52. void ProcessAllFiles();
  53. void BuildCRCFile();
  54. void CheckFiles();
  55. unsigned long CalculateBufferCRC();
  56. void BuildCRCTable();
  57.  
  58. #else /* not UNIX, must be MSDOS */
  59. /*
  60.  * Most MS-DOS compilers have converged on the same names for the
  61.  * structures and functions used when searching directories.
  62.  * Unfortunately, Borland C implementations still use a variant,
  63.  * which requires a few macro definitions to work around.  The
  64.  * functions work in an identical manner, so the actual
  65.  * implementation of the code is straightforward.  The addition of
  66.  * the MSDOS definition helps convince the Zortech compiler to use
  67.  * the same structure and function names as everyone else.
  68.  *
  69.  */
  70.  
  71. #define MSDOS 1
  72. #include <stdarg.h>
  73. #include <dos.h>
  74.  
  75. #define SEPARATOR "\\"
  76. #define FILENAME_SIZE FILENAME_MAX
  77.  
  78. #ifdef __TURBOC__
  79.  
  80. #include <dir.h>
  81. #define FILE_INFO                 struct ffblk
  82. #define FIND_FIRST( n, i )        findfirst( ( n ), ( i ), FA_DIREC )
  83. #define FIND_NEXT( info )         findnext( ( info ) )
  84. #define FILE_NAME( info )         ( ( info ).ff_name )
  85.  
  86. #else
  87.  
  88. #define FILE_INFO                 struct find_t
  89. #define FIND_FIRST( n, i )        _dos_findfirst( (n), _A_SUBDIR, (i) )
  90. #define FIND_NEXT( info )         _dos_findnext( ( info ) )
  91. #define FILE_NAME( info )         ( ( info ).name )
  92.  
  93. #endif
  94.  
  95. void FatalError( char *fmt, ... );
  96. unsigned long CalculateFileCRC( FILE *file );
  97. void ProcessAllFiles( char *path, FILE *crc_file );
  98. void BuildCRCFile( char *input_dir_name, char *crc_file_name );
  99. void CheckFiles( char *crc_file_name );
  100. unsigned long CalculateBufferCRC( unsigned int count, unsigned long crc,
  101.                                   void *buffer );
  102. void BuildCRCTable( void );
  103.  
  104. #endif  /* UNIX */
  105.  
  106. /*
  107.  * The main program is fairly simple.  It checks for valid occurences
  108.  * of the two different types of command lines, and executes them if
  109.  * found.  Otherwise, it prints out a simple usage statement and exits.
  110.  */
  111.  
  112. int main( argc, argv )
  113. int argc;
  114. char *argv[];
  115. {
  116.     setbuf( stdout, NULL );
  117.     BuildCRCTable();
  118.     if ( argc == 2 )
  119.         CheckFiles( argv[ 1 ] );
  120.     else if ( argc == 4 && strcmp( argv[ 1 ], "-b" ) == 0 )
  121.         BuildCRCFile( argv[ 2 ], argv[ 3 ] );
  122.     else {
  123.         printf( "Usage: CRCMAN [-b input_dir] crc-file \n" );
  124.         printf( "\n" );
  125.         printf( "Using the -b option checks all files under the input_dir\n" );
  126.         printf( "and appends their data to the crc-file.  Otherwise, the\n" );
  127.         printf( "program checks the CRC data of all of the files in the\n" );
  128.         printf( "crc-file and prints the results\n" );
  129.         return( 1 );
  130.     }
  131.     return( 0 );
  132. }
  133.  
  134. /*
  135.  * Instead of performing a straightforward calculation of the 32 bit
  136.  * CRC using a series of logical operations, this program uses the
  137.  * faster table lookup method.  This routine is called once when the
  138.  * program starts up to build the table which will be used later
  139.  * when calculating the CRC values.
  140.  */
  141.  
  142. #define CRC32_POLYNOMIAL     0xEDB88320L
  143.  
  144. void BuildCRCTable()
  145. {
  146.     int i;
  147.     int j;
  148.     unsigned long crc;
  149.  
  150.     for ( i = 0; i <= 255 ; i++ ) {
  151.         crc = i;
  152.         for ( j = 8 ; j > 0; j-- ) {
  153.             if ( crc & 1 )
  154.                 crc = ( crc >> 1 ) ^ CRC32_POLYNOMIAL;
  155.             else
  156.                 crc >>= 1;
  157.         }
  158.         CRCTable[ i ] = crc;
  159.     }
  160. }
  161.  
  162. /*
  163.  * The routine to check the CRC values for a list of files has a
  164.  * fairly easy job of it.  It just reads in a line at a time from
  165.  * the CRC file.  Each line contains a file name and a CRC value.
  166.  * The program then just has to calculate the actual CRC for that
  167.  * file, and compare it with the current calculated value.  Any
  168.  * discrepancy triggers an error message.
  169.  */
  170.  
  171. void CheckFiles( crc_file_name )
  172. char *crc_file_name;
  173. {
  174.     FILE *crc_file;
  175.     FILE *test_file;
  176.     unsigned long log_crc;
  177.     unsigned long crc;
  178.     char log_name[ FILENAME_SIZE ];
  179.     int result;
  180.  
  181.     crc_file = fopen( crc_file_name, "r" );
  182.     if ( crc_file == NULL )
  183.         FatalError( "Couldn't open the log file: %s\n", crc_file_name );
  184.     for ( ; ; ) {
  185.         result = fscanf( crc_file, "%lx %s", &log_crc, log_name );
  186.         if ( result < 2 )
  187.             break;
  188.         test_file = fopen( log_name, "rb" );
  189.         if ( test_file != NULL ) {
  190.             printf( "Checking %s ", log_name  );
  191.             crc = CalculateFileCRC( test_file );
  192.             fclose( test_file );
  193.             if ( crc != log_crc )
  194.                 printf( "Error:  Expected %08lx, got %08lx\n",
  195.                         log_name, log_crc, crc );
  196.             else
  197.                 printf( "OK\n" );
  198.         } else
  199.             printf( "Could not open file %s\n", log_name );
  200.     }
  201. }
  202.  
  203. /*
  204.  * Building the CRC file is a little harder than just checking
  205.  * the file values.  This is because the build operation has to
  206.  * parse through a directory tree, checking every file.  This
  207.  * routine defers the hard part of that to a routine called
  208.  * ProcessAllFiles(), which takes care of scanning through the
  209.  * directory.  That means all we have to do here is open the output
  210.  * CRC file, and then start the processing.  This routine also makes
  211.  * sure that the directory name passed on the command line is
  212.  * stripped of any trailing '/' or '\' character, since people tend
  213.  * to include those when specifying directory names.
  214.  */
  215.  
  216. void BuildCRCFile( input_dir_name, crc_file_name )
  217. char *input_dir_name;
  218. char *crc_file_name;
  219. {
  220.     char path[ FILENAME_SIZE ];
  221.     FILE *crc_file;
  222.  
  223.     strcpy( path, input_dir_name );
  224.     if ( path[ strlen( path ) - 1 ] == SEPARATOR[ 0 ] )
  225.         path[ strlen( path ) - 1 ] = '\0';
  226.     crc_file = fopen( crc_file_name, "w" );
  227.     if ( crc_file == NULL )
  228.         FatalError( "Can't open crc log file: %s\n", crc_file_name );
  229.     ProcessAllFiles( path, crc_file );
  230. }
  231.  
  232. /*
  233.  * This routine is responsible for actually performing the
  234.  * calculation of the 32 bit CRC for the entire file.  We
  235.  * precondition the CRC value with all 1's, then invert every bit
  236.  * after the entire file has been done.  This gives us a CRC value
  237.  * that corresponds with the values calculated by PKZIP and ARJ.
  238.  * The actual calculation consists of reading in blocks from the
  239.  * file, then updating the CRC with the value for that block.  The
  240.  * CRC work is done by another the CalculateBufferCRC routine.
  241.  */
  242.  
  243. unsigned long CalculateFileCRC( file )
  244. FILE *file;
  245. {
  246.     unsigned long crc;
  247.     int count;
  248.     unsigned char buffer[ 512 ];
  249.     int i;
  250.  
  251.     crc = 0xFFFFFFFFL;
  252.     i = 0;
  253.     for ( ; ; ) {
  254.         count = fread( buffer, 1, 512, file );
  255.         if ( ( i++ % 32 ) == 0 )
  256.             putc( '.', stdout );
  257.         if ( count == 0 )
  258.             break;
  259.         crc = CalculateBufferCRC( count, crc, buffer );
  260.     }
  261.     putc( ' ', stdout );
  262.     return( crc ^= 0xFFFFFFFFL );
  263. }
  264.  
  265. /*
  266.  * This is the routine that is responsible for calculating all of
  267.  * the CRC values for the files in a given directory.  The CRC
  268.  * values and the file names are written out to the crc_file.  This
  269.  * routine is somewhat ugly and hard to read because of all the
  270.  * conditional code.  When searching through directories under
  271.  * MS-DOS and UNIX, the flow of control is identical, but all of the
  272.  * function calls, structure names, and elements are different.
  273.  *
  274.  * This routine sits in a loop for each directory, opening each file
  275.  * and processing it.  Before a file is opened, a check is made to
  276.  * see if the file is actually a directory.  If it turns out that
  277.  * the file is a directory, a new path name is constructed, and this
  278.  * routine calls itself recursively so that all the files in the
  279.  * subdirectory are also processed.
  280.  */
  281.  
  282. void ProcessAllFiles( path, crc_file )
  283. char *path;
  284. FILE *crc_file;
  285. {
  286. #ifdef UNIX
  287.     DIR *dirp;
  288. #ifdef M_XENIX
  289.     struct direct *entry;
  290. #else
  291.     struct dirent *entry;
  292. #endif /* M_XENIX */
  293. #define NAME entry->d_name
  294. #else
  295.     FILE_INFO fileinfo;
  296.     int done;
  297. #define NAME FILE_NAME( fileinfo )
  298. #endif
  299.     char fullname[ FILENAME_SIZE ];
  300.     struct stat buf;
  301.     unsigned long crc;
  302.     FILE *file;
  303.  
  304.     printf( "Searching %s\n", path );
  305.     strcat( path, SEPARATOR );
  306. #ifdef UNIX
  307.     dirp = opendir( path );
  308.     if ( dirp == NULL )
  309.         FatalError( "Error opening directory %s\n", path );
  310.     entry = readdir( dirp );
  311.     while ( entry != 0 ) {
  312. #else
  313.     strcpy( fullname, path );
  314.     strcat( fullname, "*.*" );
  315.     done = FIND_FIRST( fullname, &fileinfo );
  316.     while ( done == 0 ) {
  317. #endif
  318.         strcpy( fullname, path );
  319.         if ( strcmp( NAME, "." ) && strcmp( NAME, ".." ) ) {
  320.             strcat( fullname, NAME );
  321.             if ( stat( fullname, &buf ) == -1 )
  322.                 FatalError( "Error reading stat from file %s!\n", fullname );
  323.             if ( buf.st_mode & S_IFDIR )
  324.                 ProcessAllFiles( fullname, crc_file );
  325.             else {
  326.                 file = fopen( fullname, "rb" );
  327.                 if ( file != NULL ) {
  328.                     printf( "Scanning %s ", fullname  );
  329.                     crc = CalculateFileCRC( file );
  330.                     putc( '\n', stdout );
  331.                     fprintf( crc_file, "%08lx %s\n", crc, fullname );
  332.                     fclose( file );
  333.                } else
  334.                     printf( "Could not open %s!\n", fullname );
  335.             }
  336.         }
  337. #ifdef UNIX
  338.         entry = readdir( dirp );
  339. #else
  340.         done = FIND_NEXT( &fileinfo );
  341. #endif
  342.     }
  343. }
  344.  
  345. /*
  346.  * This routine calculates the CRC for a block of data using the
  347.  * table lookup method. It accepts an original value for the crc,
  348.  * and returns the updated value.
  349.  */
  350.  
  351. unsigned long CalculateBufferCRC( count, crc, buffer )
  352. unsigned int count;
  353. unsigned long crc;
  354. void *buffer;
  355. {
  356.     unsigned char *p;
  357.     unsigned long temp1;
  358.     unsigned long temp2;
  359.  
  360.     p = (unsigned char*) buffer;
  361.     while ( count-- != 0 ) {
  362.         temp1 = ( crc >> 8 ) & 0x00FFFFFFL;
  363.         temp2 = CRCTable[ ( (int) crc ^ *p++ ) & 0xff ];
  364.         crc = temp1 ^ temp2;
  365.     }
  366.     return( crc );
  367. }
  368.  
  369. /*
  370.  * The fatal error handler just has to print out a formatted error
  371.  * message and then exit.  The difficulty in this routine lies in
  372.  * the different way that variable numbers of arguments are
  373.  * processed under ANSI C and K&R C.  The body of the routine is
  374.  * the same under both environments, but the declaration of the
  375.  * function and its arguments differs quite a bit.
  376.  */
  377.  
  378. #ifdef UNIX
  379.  
  380. void FatalError( va_alist )
  381. va_dcl
  382. {
  383.     char *fmt;
  384.     va_list argptr;
  385.  
  386.     va_start( argptr );
  387.     fmt = va_arg( argptr, char * );
  388.  
  389. #else
  390.  
  391. void FatalError( char *fmt, ... )
  392. {
  393.     va_list argptr;
  394.     va_start( argptr, fmt );
  395.  
  396. #endif
  397.  
  398.     printf( "Fatal error: " );
  399.     vprintf( fmt, argptr );
  400.     va_end( argptr );
  401.     exit( -1 );
  402. }
  403. /************************** End of CRCMAN.C ****************************/
  404.  
  405.